/*
 *  Copyright (c) 2009, Hugo Augusto Alves
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of the Hugo Augusto Alves nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.liteml.parser.blocks;

import org.liteml.exceptions.LiteParseException;
import org.liteml.parser.LiteUtils;

/**
 *
 * @author Hugo
 */
public class LiteTable extends LiteBlock {

    private LiteTableCell[][] table;
    private String caption = null;

    @Override
    public Type getType() {
        return Type.TABLE;
    }

    @Override
    public String parse(LiteUtils.Target t) {
        switch (t) {
            case HTML:

                StringBuilder sb = new StringBuilder();
                sb.append("<table>");
                if (caption != null) {
                    sb.append("<caption>");
                    sb.append(caption);
                    sb.append("</caption>");
                }
                for (int i = 0; i < table.length; i++) {
                    sb.append("<tr>");
                    for (int j = 0; j < table[0].length; j++) {

                        if (table[i][j] != null) {
                            sb.append("<t");
                            if (table[i][j].isHeader()) {
                                sb.append("h");
                            } else {
                                sb.append("d");
                            }
                            if (table[i][j].getColspan() > 1) {
                                sb.append(" colspan=\"");
                                sb.append(table[i][j].getColspan());
                                sb.append("\"");
                            }

                            if (table[i][j].getRowspan() > 1) {
                                sb.append(" rowspan=\"");
                                sb.append(table[i][j].getRowspan());
                                sb.append("\"");
                            }

                            sb.append(">");
                            sb.append(table[i][j].getText());
                            if (table[i][j].isHeader()) {
                                sb.append("</th>");
                            } else {
                                sb.append("</td>");
                            }


                        }
                    }
                    sb.append("</tr>");
                }
                sb.append("</table>");

                return sb.toString();
            case PLAIN:
            default:
                return "";
        }
    }

    LiteTable(String str) throws LiteParseException {
        String[] rows = str.split("\n");

        int rowCount = rows.length;
        for (int i = 0; i < rows.length; i++) {
            if (rows[i].startsWith(LiteUtils.TABLE_LABEL_CELL)) {
                rowCount--;
                break;
            }
        }
        int colspanChars = 0;
        int colspanBlocks = 0;
        for (int i = 0; i < rows[0].length(); i++) {
            if (rows[0].charAt(i) == LiteUtils.CHAR_TABLE_COLSPAN) {
                colspanChars++;
                if (i == 0) {
                    // first block of colspan chars
                    colspanBlocks++;
                } else {
                    // if the previous char is not a colspan char then there is another colspan block
                    if (rows[0].charAt(i - 1) != LiteUtils.CHAR_TABLE_COLSPAN) {
                        colspanBlocks++;
                    }
                }
            }
        }
        // number of actuals rows + number of colspan chars - number of colspan blocks
        int colCount = (rows[0].split(LiteUtils.TABLE_SEPARATOR_REGEX).length - 1) + (colspanChars - colspanBlocks);

        if (colCount <= 0 || rowCount <= 0) {
            throw new LiteParseException("Invalid table block: " + str);
        }

        table = new LiteTableCell[rowCount][colCount];
        for (int i = 0; i < rows.length; i++) {
            processRow(rows[i], i);
        }
        computeRowspans();
    }

    private void processRow(String line, int i) throws LiteParseException {

        String[] cells = line.split(LiteUtils.TABLE_SEPARATOR_REGEX);
        if (cells.length <= 0) {
            throw new LiteParseException("Invalid table line: " + line);
        }

        int j = 0;
        // first element is empty - see split
        for (int idx = 1; idx < cells.length; idx++) {
            int colspan = processCell(cells[idx], i, j);
            if (colspan > 1) {
                j += colspan;
            } else {
                j++;
            }
        }

    }

    /**
     * process the cell
     * @param ctxt
     * @param i
     * @param j
     * @return the colspan of current cell
     */
    private int processCell(String ctxt, int i, int j) throws LiteParseException {

        if (ctxt == null) {
            throw new LiteParseException("Invalid table cell: " + ctxt);
        }

        if (!ctxt.isEmpty() && ctxt.charAt(0) == LiteUtils.CHAR_TABLE_LABEL) {
            caption = ctxt.substring(1).trim();
            return 0;
        }

        if (i >= table.length || j >= table[0].length) {
            throw new LiteParseException("Invalid table indexes: " + i + " " + j);
        }
        
        LiteTableCell cell = new LiteTableCell();
        StringBuilder sb = new StringBuilder(ctxt);
        boolean modifiers = true;
        for (int idx = 0; idx < sb.length(); idx++) {
            char c = sb.charAt(idx);
            if (modifiers) {
                if (c == LiteUtils.CHAR_TABLE_ROWSPAN) {
                    cell.setRowspan(1);
                } else if (c == LiteUtils.CHAR_TABLE_COLSPAN) {
                    cell.incrementColspan();
                } else if (c == LiteUtils.CHAR_TABLE_HEADER) {
                    cell.setHeader(true);
                } else {
                    modifiers = false;
                }
            } else {
                cell.setText(sb.substring(idx - 1));
                break;
            }
        }
        table[i][j] = cell;
        return cell.getColspan();
    }

    private void computeRowspans() throws LiteParseException {
        for (int i = 0; i < table.length; i++) {
            for (int j = 0; j < table[0].length; j++) {
                if (table[i][j] != null && table[i][j].getRowspan() > 0) {
                    int idx = i + 1;
                    while (idx < table.length) {
                        if (table[idx][j] == null) {
                            break;
                        }
                        if (table[idx][j].getRowspan() > 0 && table[idx][j].getText().equals("")) {
                            table[i][j].incrementRowspan();
                            table[idx][j] = null;
                        }
                        idx++;
                    }
                }
            }
        }
    }
}
